package auctionsimulation.market;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import auctionsimulation.agent.Agent;
import auctionsimulation.helper.AuctionHelper;
import auctionsimulation.statistics.Statistics;

public class MarketServer implements Runnable {

	private CopyOnWriteArrayList<ReverseEnglishAuction> reverseEnglishAuctions;
	private CopyOnWriteArrayList<DutchAuction> dutchAuctions;
	private CopyOnWriteArrayList<DoubleAuction> doubleAuctions;

	public MarketServer() {
		this.reverseEnglishAuctions = new CopyOnWriteArrayList<ReverseEnglishAuction>();
		this.dutchAuctions = new CopyOnWriteArrayList<DutchAuction>();
		this.doubleAuctions = new CopyOnWriteArrayList<DoubleAuction>();
	}

	public CopyOnWriteArrayList<ReverseEnglishAuction> getReverseEnglishAuctions() {
		return this.reverseEnglishAuctions;
	}

	public CopyOnWriteArrayList<DutchAuction> getDutchAuctions() {
		return this.dutchAuctions;
	}

	public CopyOnWriteArrayList<DoubleAuction> getDoubleAuctions() {
		return this.doubleAuctions;
	}

	@Override
	public void run() {
		ExecutorService executor = Executors.newCachedThreadPool();
		ServerSocket servSock = null;
		try {
			servSock = new ServerSocket(1234);
			System.out.print("MarketServer is ready to dispatch commands!\n");
			for (;;) {
				try {
					Runnable worker = new MarketWorker(this, servSock.accept());
					executor.execute(worker);
				} catch (IOException e) {
					AuctionHelper.error(
							"was not able to start new MarketWorker", this, e);
				}
			}
		} catch (IOException e) {
			AuctionHelper.error("server could not establish TCP connection",
					this, e);
		} finally {
			if (servSock != null) {
				try {
					servSock.close();
				} catch (IOException e) {
					AuctionHelper.error(
							"server was not able to close serversocket", this,
							e);
				}
			}
		}
	}

	public boolean addAuction(Auction a) {
		if (a instanceof ReverseEnglishAuction) {
			this.reverseEnglishAuctions.add((ReverseEnglishAuction) a);
			a.start();
			return true;
		} else if (a instanceof DutchAuction) {
			this.dutchAuctions.add((DutchAuction) a);
			a.start();
			return true;
		} else if (a instanceof DoubleAuction) {
			this.doubleAuctions.add((DoubleAuction) a);
			a.start();
			return true;
		}
		return false;
	}

	public boolean buyAuction(Auction a) {
		Statistics.matchedAuctions++;
		if (a instanceof ReverseEnglishAuction) {
			Auction wanted = this.getAuctionByUUID(this.reverseEnglishAuctions,
					a.getUUID());
			if (wanted == null) {
				return false;
			}
			Statistics.matchedReverseEnglishAuctions++;
			return ((ReverseEnglishAuction) wanted).buy();
		} else if (a instanceof DutchAuction) {
			Auction wanted = this.getAuctionByUUID(this.dutchAuctions,
					a.getUUID());
			if (wanted == null) {
				return false;
			}
			Statistics.matchedDutchAuctions++;
			return ((DutchAuction) wanted).buy();
		}
		return false;
	}

	public boolean placePriceBid(String auctionType, Agent seller,
			double offeredPrice, HashMap<String, Long> offeredTemplate) {
		switch (auctionType) {
		case "reverse":
			for (ReverseEnglishAuction auction : this.reverseEnglishAuctions) {
				if (auction
						.placePriceBid(seller, offeredPrice, offeredTemplate)) {
					return true;
				}
			}
			return false;
		case "double":
			for (DoubleAuction auction : this.doubleAuctions) {
				if (auction
						.placePriceBid(seller, offeredPrice, offeredTemplate)) {
					return true;
				}
			}
		}
		return false;
	}
	
	public boolean bidForDoubleAuction(Agent bidder, Auction a, double price) {
		
		Auction wanted = this.getAuctionByUUID(this.doubleAuctions, a.getUUID());
		if(wanted == null) {
			return false;
		}
		Statistics.matchedAuctions++;
		Statistics.matchedDoubleAuctions++;
		
		DoubleAuction bidFor = (DoubleAuction) wanted;
		return bidFor.placeBid(bidder, price);
	}

	private Auction getAuctionByUUID(
			CopyOnWriteArrayList<? extends Auction> list, long UUID) {
		for (Auction a : list) {
			if (a.getUUID() == UUID) {
				return a;
			}
		}
		return null;
	}
}
